﻿using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace _3389ServerManager.Encrypt
{
    public class Rdp
    {
        #region Dll引用
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct CRYPTPROTECT_PROMPTSTRUCT
        {
            public int cbSize;
            public int dwPromptFlags;
            public IntPtr hwndApp;
            public string szPrompt;
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        internal struct DATA_BLOB
        {
            public int cbData;
            public IntPtr pbData;
        }

        public enum KeyType
        {
            UserKey = 1, MachineKey
        }

        [DllImport("kernel32")]
        private static extern long WritePrivateProfileString(string section, string key, string val, string filePath);

        [DllImport("kernel32")]
        private static extern int GetPrivateProfileString(string section, string key, string def, StringBuilder retVal, int size, string filePath);

        [DllImport("crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool CryptProtectData(ref DATA_BLOB pPlainText, string szDescription, ref DATA_BLOB pEntropy, IntPtr pReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPrompt, int dwFlags, ref DATA_BLOB pCipherText);

        [DllImport("crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern bool CryptUnprotectData(ref DATA_BLOB pCipherText, ref string pszDescription, ref DATA_BLOB pEntropy, IntPtr pReserved, ref CRYPTPROTECT_PROMPTSTRUCT pPrompt, int dwFlags, ref DATA_BLOB pPlainText);
        #endregion

        private static readonly IntPtr NullPtr = ((IntPtr)((0)));
        private const int CRYPTPROTECT_UI_FORBIDDEN = 0 * 1;
        private const int CRYPTPROTECT_LOCAL_MACHINE = 0 * 4;
        StringBuilder temp = new StringBuilder(255);

        public static byte[] DoRawEncryption(string strToEncrypt)
        {
            byte[] cryptBytes = Encrypt(KeyType.UserKey, Encoding.UTF8.GetBytes(strToEncrypt), null, null);
            return cryptBytes;
        }

        public static byte[] Encrypt(KeyType keyType, byte[] plainTextBytes, byte[] entropyBytes, string description)
        {
            if (plainTextBytes == null) plainTextBytes = new byte[0];
            if (entropyBytes == null) entropyBytes = new byte[0];
            if (description == null) description = String.Empty;
            DATA_BLOB plainTextBlob = new DATA_BLOB();
            DATA_BLOB cipherTextBlob = new DATA_BLOB();
            DATA_BLOB entropyBlob = new DATA_BLOB();
            CRYPTPROTECT_PROMPTSTRUCT prompt = new CRYPTPROTECT_PROMPTSTRUCT();
            InitPrompt(ref prompt);

            try
            {
                try
                {
                    InitBLOB(plainTextBytes, ref plainTextBlob);
                }
                catch (Exception ex)
                {
                    throw new Exception("Cannot initialize plaintext BLOB.", ex);
                }
                try
                {
                    InitBLOB(entropyBytes, ref entropyBlob);
                }
                catch (Exception ex)
                {
                    throw new Exception("Cannot initialize entropy BLOB.", ex);
                }
                int flags = CRYPTPROTECT_UI_FORBIDDEN;
                if (keyType == KeyType.MachineKey)
                    flags |= CRYPTPROTECT_LOCAL_MACHINE;
                bool success = CryptProtectData(ref plainTextBlob, description, ref entropyBlob, IntPtr.Zero, ref prompt, flags, ref cipherTextBlob);
                if (!success)
                {
                    int errCode = Marshal.GetLastWin32Error();
                    throw new Exception("CryptProtectData failed.");
                }

                byte[] cipherTextBytes = new byte[cipherTextBlob.cbData];

                Marshal.Copy(cipherTextBlob.pbData, cipherTextBytes, 0, cipherTextBlob.cbData);

                return cipherTextBytes;
            }
            catch (Exception ex)
            {
                throw new Exception("DPAPI was unable to encrypt data.", ex);
            }
            finally
            {
                if (plainTextBlob.pbData != IntPtr.Zero)
                    Marshal.FreeHGlobal(plainTextBlob.pbData);

                if (cipherTextBlob.pbData != IntPtr.Zero)
                    Marshal.FreeHGlobal(cipherTextBlob.pbData);

                if (entropyBlob.pbData != IntPtr.Zero)
                    Marshal.FreeHGlobal(entropyBlob.pbData);
            }
        }

        
        private static void InitPrompt(ref CRYPTPROTECT_PROMPTSTRUCT ps)
        {
            ps.cbSize = Marshal.SizeOf(typeof(CRYPTPROTECT_PROMPTSTRUCT));
            ps.dwPromptFlags = 0;
            ps.hwndApp = NullPtr;
            ps.szPrompt = null;
        }

        private static void InitBLOB(byte[] data, ref DATA_BLOB blob)
        {
            byte[] tmp = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, data);
            blob.pbData = Marshal.AllocHGlobal(tmp.Length);
            if (blob.pbData == IntPtr.Zero)
                throw new Exception("Unable to allocate data buffer for BLOB structure.");
            blob.cbData = tmp.Length;
            Marshal.Copy(tmp, 0, blob.pbData, tmp.Length);
        }

        /// <summary>
        /// 生成RDP密码
        /// </summary>
        /// <param name="RdpPassword">密码</param>
        /// <returns></returns>
        public static string GenerateRdpPassword(string RdpPassword)
        {
            string EncryptedPassword;
            byte[] cryptBytes = DoRawEncryption(RdpPassword);
            EncryptedPassword = Encoding.Unicode.GetString(cryptBytes);
            EncryptedPassword = BitConverter.ToString(cryptBytes).Replace("-", "");
            //写INI配置("Data", "mima2", EncryptedPassword, 取运行目录() + "\\config.ini");
            return EncryptedPassword;
        }

        #region 生成临时文件
        /// <summary>
        /// 生成临时文件
        /// </summary>
        /// <param name="ServerIp">服务器地址</param>
        /// <param name="ServerPort">服务器端口</param>
        /// <param name="Users">用户名</param>
        /// <param name="Password">密码</param>
        public static void GenerateTemp(string ServerIp, string ServerPort, string Users, string Password)
        {
            string TempName = Path.GetTempFileName();
            string TempText = @"screen mode id:i:1
                            desktopwidth:i:800
                            desktopheight:i:600
                            session bpp:i:16
                            winposstr:s:0,3,0,0,800,600
                            compression:i:1
                            keyboardhook:i:2
                            displayconnectionbar:i:1
                            username:s:@Account
                            disable wallpaper:i:1
                            disable full window drag:i:1
                            allow desktop composition:i:0
                            allow font smoothing:i:0
                            disable menu anims:i:1
                            disable themes:i:0
                            disable cursor setting:i:0
                            bitmapcachepersistenable:i:1
                            full address:s:@ServerAddress
                            @PassWord
                            audiomode:i:0
                            redirectprinters:i:1
                            redirectcomports:i:0
                            redirectsmartcards:i:1
                            redirectclipboard:i:1
                            redirectposdevices:i:0
                            drivestoredirect:s:
                            autoreconnection enabled:i:1
                            authentication level:i:0
                            prompt for credentials:i:0
                            negotiate security layer:i:1
                            remoteapplicationmode:i:0
                            alternate shell:s:
                            shell working directory:s:
                            gatewayhostname:s:
                            gatewayusagemethod:i:4
                            gatewaycredentialssource:i:4
                            gatewayprofileusagemethod:i:0
                            promptcredentialonce:i:1";
            TempText = TempText.Replace("@Account", Users);
            TempText = TempText.Replace("@ServerAddress", ServerIp + ":" + ServerPort);
            TempText = TempText.Replace("@PassWord", "password 51:b:" + GenerateRdpPassword(Password));
            //WritingFiles(TempName, Template);
        }
        #endregion

        //#region 登陆RDP服务器
        ///// <summary>
        ///// 登陆RDP服务器
        ///// </summary>
        ///// <param name="ConfigModel">服务器信息</param>
        ///// <param name="StartupMode">启动方式</param>
        ///// <param name="Resolution">分辨率</param>
        ////启动方式,分辨率,当前选中项
        //public static void Lgoin(Model.ConfigModel ConfigModel, string StartupMode, string Resolution)
        //{
        //    try
        //    {
        //        if (Resolution == "1")
        //        {
        //            Resolution = " /w:800 /h:600 ";
        //        }
        //        if (Resolution == "2")
        //        {
        //            Resolution = " /w:1024 /h:768 ";
        //        }
        //        if (Resolution == "3")
        //        {
        //            Resolution = " /f ";
        //        }
        //        if (StartupMode == "0")
        //        {
        //            StartupMode = " /console ";
        //        }
        //        if (StartupMode == "1")
        //        {
        //            StartupMode = " /admin ";
        //        }
        //        GenerateTemp(
        //            ConfigModel.ServerIp,
        //            ConfigModel.ServerPort,
        //            ConfigModel.Users,
        //            ConfigModel.Password
        //            );
        //        Process.Start("mstsc.exe ", "\"" + TempName + "\"" + StartupMode + Resolution);
        //    }
        //    catch (Exception ex)
        //    {
        //        throw ex;
        //    }
        //}
        //#endregion
    }
}
